using System;
using System.Collections.Generic;
using System.Text;
using NBusiness.CodeDom;
using NBusiness.CodeDom.Compiler;
using NBusiness.ESharp.Compiler;
using NBusiness.Utilities;
using System.Reflection;

namespace NBusiness.ESharp
{
    [Serializable]
    public class ESharpRoot : EntityRoot
    {

        public ESharpRoot(EntityProvider provider)
            : base(provider)
        { }

        #region Attach
        protected override void Attach()
        {
            foreach (EntityFamily family in Families.Values)
                (family as ICompileUnit).Attach();
        } 
        #endregion

        #region initialize
        protected override void InitializeAssignmentLogic()
        {
            Assignments.Add(new AssignmentLogic( ESharpEntityElementTypes.FamilyDeclaration ));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.FamilyName,
                new AssignmentLogicHandler(BuildFamily) ));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.UsingDeclaration,
                new AssignmentLogicHandler(BuildUsing) ));

            Assignments.Add(new AssignmentLogic( ESharpEntityElementTypes.WhiteSpace ));
            Assignments.Add(new AssignmentLogic( ESharpEntityElementTypes.Comment ));
        } 
        #endregion

        #region Build elements

        private void BuildFamily(object sender, AssignmentLogicEventArgs e)
        {
            EntityFamily family = null;
            string name = e.Elements.Peek().Value;
            if (Families.ContainsKey(name))
            {
                family = Families[name];
            }
            else
            {
                family = new ESharpFamily();
                family.Name = name;
                family.Parent = this;
                Families.Add(name, family);
            }

            (family as ICompileUnit).Build(e.Elements, ESharpEntityElementTypes.FamilyName);
        }

        private void BuildUsing(object sender, AssignmentLogicEventArgs e)
        {
            string path = e.Elements.Peek().Path;

            ESharpUsing entityUsing = new ESharpUsing();
            entityUsing.Root = this;
            (entityUsing as ICompileUnit).Build(e.Elements, ESharpEntityElementTypes.UsingDeclaration);

            if (!Using.ContainsKey(path ?? ""))
                Using.Add(path ?? "", new List<EntityUsing>());

            Using[path ?? ""].Add(entityUsing);
        }

        #endregion

        #region Get element from child

        protected override EntityElement GetElementFromChild(string path, int line, int col)
        {
            EntityElement e = null;
            foreach (EntityFamily f in Families.Values)
            {
                e = (f as ICompileUnit).GetElementByLocation(path, line, col);
                if (e != null) break;
            }

            return e;
        } 
        #endregion

        #region GetUnitFromChild
        protected override ICompileUnit GetUnitFromChild(EntityElement element)
        {
            ICompileUnit unit = null;
            foreach (EntityFamily family in Families.Values)
            {
                unit = (family as ICompileUnit).GetUnitByElement(element);
                if (unit != null) break;
            }

            return unit;
        } 
        #endregion

        #region Helpers
        internal static int GetLine(string code, int index)
        {
            int line = 0;
            int i = code.IndexOf("\n");

            while (i > 0 && i < index)
            {
                line++;
                i = code.IndexOf("\n", i + 1);
            }
            return line;
        }
        internal static int GetColumn(string code, int index)
        {
            int col = 0;
            while (index - col > 0 && code[index - col - 1] != '\n')
            {
                col++;
            }
            return col;
        } 
        #endregion

        #region Find By Path
        public Entity[] FindByPath(string path)
        {
            List<Entity> found = new List<Entity>();
            foreach (EntityFamily family in Families.Values)
            {
                found.AddRange(FindByPath(path, family));
            }

            return found.ToArray();
        }

        private Entity[] FindByPath(string path, EntityFamily family)
        {
            List<Entity> found = new List<Entity>();
            foreach (EntityFamily subFamily in family.Families.Values)
            {
                found.AddRange(FindByPath(path, subFamily));
            }

            found.AddRange(family.Entities.FindAll(e => (e as ICompileUnit).Elements[0].Path == path));
            
            return found.ToArray();
        } 
        #endregion
    }
}
